import { v4 as uuidv4 } from 'uuid';
import type { FlowNode, FlowEdge, JDFAttribute } from '@/models/jdf';
import type { ParseResult } from './types';
import { XmlDocument } from 'libxml2-wasm'; // Assuming libxml2-wasm is globally available or importable

// Placeholder for libxml2 node type - replace with actual type from libxml2-wasm if available
type LibXml2Node = any;

/**
 * Parses XML content using the libxml2-wasm library.
 *
 * @param content The XML string content.
 * @returns A ParseResult object.
 */
export function parse(content: string): ParseResult {
    let doc: XmlDocument | null = null;
    try {
        // Attempt to parse the XML string
        doc = XmlDocument.fromString(content);

        // Access root as a property
        const rootElement = doc.root;
        if (!rootElement) {
            throw new Error('No root element found in libxml2 document.');
        }

        // Extract flow elements using libxml2 traversal
        const { nodes, edges } = extractElementsFromLibXml2(rootElement);

        // Note: Keeping the libxml2 document object might require careful memory management (dispose)
        // For simplicity, we might not store it in the result unless specifically needed.

        return {
            success: true,
            document: null, // Or store doc if disposal is handled
            nodes,
            edges
        };

    } catch (err: any) {
        console.error('[Libxml2WasmWrapper] Error:', err);
        // Try to extract line/column from the error object if they exist
        const lineMatch = err.message?.match(/line[\s:=]+(\d+)/i);
        const colMatch = err.message?.match(/col(?:umn)?[\s:=]+(\d+)/i);
        const line = err.line || (lineMatch ? parseInt(lineMatch[1], 10) : 'N/A');
        const column = err.column || (colMatch ? parseInt(colMatch[1], 10) : 'N/A');
        const message = err.message?.split('\n')[0] || 'An unexpected error occurred.';

        return {
            success: false,
            error: `libxml2 Error: ${message} (Line: ${line}, Col: ${column})`,
            nodes: [],
            edges: []
        };
    } finally {
        // IMPORTANT: Dispose the document to free up WASM memory
        if (doc) {
            try {
                doc.dispose();
                console.log('[libxml2-wasm] Document disposed in parser.');
            } catch (disposeErr) {
                console.error('[libxml2-wasm] Error disposing document:', disposeErr);
            }
        }
    }
}

// --- Helper functions specific to libxml2 traversal ---

/**
 * Extracts FlowNode and FlowEdge arrays by traversing the libxml2 node tree.
 * @param rootLibXmlNode The root node object from libxml2.
 * @returns An object containing the extracted nodes and edges.
 */
function extractElementsFromLibXml2(rootLibXmlNode: LibXml2Node): { nodes: FlowNode[]; edges: FlowEdge[] } {
    const nodes: FlowNode[] = [];
    const edges: FlowEdge[] = [];

    // Start recursive processing
    processLibXml2Node(rootLibXmlNode, null, nodes, edges, 0);

    return { nodes, edges };
}

/**
 * Recursively processes a libxml2 node object to create FlowNode and FlowEdge objects.
 *
 * @param lxNode The current libxml2 node object.
 * @param parentId The ID of the parent FlowNode (or null for root).
 * @param nodes The array to add created FlowNodes to.
 * @param edges The array to add created FlowEdges to.
 * @param level The current depth in the tree.
 * @returns The generated ID of the created FlowNode.
 */
function processLibXml2Node(lxNode: LibXml2Node, parentId: string | null, nodes: FlowNode[], edges: FlowEdge[], level: number): string {
    // Determine node kind using constructor name
    const nodeKind = lxNode.constructor?.name; 

    // Skip non-element nodes based on constructor name
    if (nodeKind !== 'XmlElement') {
        return ''; // Skip non-element nodes
    }
    
    const nodeId = `node-${uuidv4()}`;

    // Get Node Name 
    const nodeName = lxNode.name ? String(lxNode.name) : 'unknown';

    // Get Attributes
    const attributes: JDFAttribute[] = [];
    if (lxNode.attrs && Array.isArray(lxNode.attrs)) {
        lxNode.attrs.forEach((attr: any) => {
            // Use property access for name and value
            const attrName = attr.name ? String(attr.name) : null;
            const attrValue = attr.value !== undefined ? String(attr.value) : undefined;
            if (attrName !== null && attrValue !== undefined) {
                attributes.push({ name: attrName, value: attrValue });
            }
        });
    }

    // Get Text Content & Find Child Elements
    let textContent = '';
    const childElements: LibXml2Node[] = [];
    let hasElementChildren = false;
    let child = lxNode.firstChild; 

    while (child) {
        const childKind = child.constructor?.name; 

        if (childKind === 'XmlElement') {
            hasElementChildren = true;
            childElements.push(child);
        } else if (childKind === 'XmlText') {
            const text = typeof child.content === 'string' ? child.content : '';
            if (text.trim()) {
                 textContent += text; 
            }
        }
        child = child.next; 
    }
    textContent = textContent.trim();

    // --- Recursively process child elements ---
    const childrenIds: string[] = [];
    childElements.forEach((childElement: LibXml2Node) => {
        const childId = processLibXml2Node(childElement, nodeId, nodes, edges, level + 1);
        if (childId) { // Ensure recursion didn't return empty string (e.g., if type check added)
             childrenIds.push(childId);
        }
    });

    // --- Create FlowNode ---
    const flowNode: FlowNode = {
        id: nodeId,
        type: 'jdfNode', // Ensure this matches your node type definition
        position: { x: 0, y: 0 }, // Layout will adjust this
        data: {
            nodeName,
            attributes,
            textContent,
            hasChildren: hasElementChildren, // Use the flag determined during iteration
            level,
            isExpanded: true, // Default state, user can toggle
            childrenIds
            // Avoid storing originalNode unless absolutely necessary & mem mgmt is guaranteed
        },
        parentId: parentId ?? undefined,
        hidden: false // Default state
    };

    nodes.push(flowNode);

    // --- Create Edge to Parent ---
    if (parentId) {
        const edgeId = `edge-${parentId}-${nodeId}`;
        const edge: FlowEdge = {
            id: edgeId,
            source: parentId,
            target: nodeId,
            type: 'smoothstep', // Or your preferred edge type
            hidden: false // Default state
        };
        edges.push(edge);
    }

    return nodeId;
} 